

package account

import (
    "errors"
    "fmt"
    "strconv"
    "strings"

    "github.com/gin-gonic/gin"
    "github.com/gin-gonic/gin/binding"
    "iip/account/model"
    "iip/account/account_type"
    "iip/lib"
    "libgo/network/gins/ginjwt"
    "libgo/security/webpass"
)

const UIDKey = "uid"


func (s *Account) Authenticator(c *gin.Context) (ginjwt.MapClaims, error) {
    var err error
    loginAuthType := struct {
        AuthType string `form:"authtype" json:"authtype" binding:"required"`
    }{}
    if err = c.ShouldBindBodyWith(&loginAuthType, binding.JSON); err != nil {
        return nil, errors.New("missing authtype")
    }

    switch account_type.AuthTypeInt(loginAuthType.AuthType) {
    case account_type.AuthTypeNamePass:
        req := &struct {
            Username string `form:"username" json:"username" binding:"required,min=3"`
            Password string `form:"password" json:"password" binding:"required"`
        }{}
        if err := c.ShouldBindBodyWith(req, binding.JSON); err != nil {
            s.logger.Error(err)
            return nil, errors.New("missing username or password")
        }
        s.logger.Info("用户登录", req)

        acc, err := s.GetAccountByName(req.Username)
        if err != nil {
            s.logger.Error(err.Error())
            return nil, errors.New("db error")
        }
        s.logger.Infof("查找到用户%s[%d]", acc.Name, acc.ID)
        auth, err := s.GetAuth(acc.ID, account_type.AuthTypeNamePass)
        if err != nil {
            s.logger.Error(err.Error())
            return nil, errors.New("db error")
        }
        if !webpass.PasswordVerify(req.Password, auth.Credential) {

            acc_log := &model.AccountLog{}
            acc_log.ID = s.idgen.NextID()
            acc_log.Type = account_type.AccountLoginFailed
            acc_log.AccountID = acc.ID
            acc_log.AccountName = acc.Name
            acc_log.IP = c.ClientIP()
            acc_log.Result = fmt.Sprintf("p=%s", req.Password)
            if err := s.gorm().Create(acc_log).Error; err != nil {
                s.logger.Error(err)
            }
            return nil, errors.New("invalid password")
        }
        s.logger.Infof("用户%s[uid=%d]登录成功", acc.Name, acc.ID)


        acc_log := &model.AccountLog{}
        acc_log.ID = s.idgen.NextID()
        acc_log.Type = account_type.AccountLoginOK
        acc_log.AccountID = acc.ID
        acc_log.AccountName = acc.Name
        acc_log.IP = c.ClientIP()
        acc_log.Result = ""
        if err := s.gorm().Create(acc_log).Error; err != nil {
            s.logger.Error(err)
        }


        return ginjwt.MapClaims{
            UIDKey: acc.ID,

        }, nil
    case account_type.AuthTypePhonePass:
        req := &struct {
            Phone    string `form:"phone" json:"phone" binding:"required,min=10"`
            Password string `form:"password" json:"password" binding:"required"`
        }{}
        if err := c.ShouldBindBodyWith(req, binding.JSON); err != nil {
            s.logger.Error(err)
            return nil, errors.New("missing phone or password")
        }
        s.logger.Infof("手机号%s登录", req.Phone)







        acc, err := s.GetAccountByPhone(req.Phone)
        if err != nil {
            s.logger.Error(err.Error())
            return nil, errors.New("db error")
        }
        s.logger.Infof("手机号%s对应账号%s[%d]", req.Phone, acc.Name, acc.ID)


        if acc.State == account_type.StateSuspend {
            err = fmt.Errorf("账号%s[%d]已经冻结", acc.Name, acc.ID)
            s.logger.Error(err)
            lib.JsonError(c, err.Error())
            return nil, err
        }

        auth, err := s.GetAuth(acc.ID, account_type.AuthTypePhonePass)
        if err != nil {
            s.logger.Error(err.Error())
            return nil, errors.New("db error")
        }
        if !webpass.PasswordVerify(req.Password, auth.Credential) {

            acc_log := &model.AccountLog{}
            acc_log.ID = s.idgen.NextID()
            acc_log.Type = account_type.AccountLoginFailed
            acc_log.AccountID = acc.ID
            acc_log.AccountName = acc.Name
            acc_log.IP = c.ClientIP()
            acc_log.Result = fmt.Sprintf("p=%s", req.Password)
            if err := s.gorm().Create(acc_log).Error; err != nil {
                s.logger.Error(err)
            }
            return nil, errors.New("invalid password")
        }
        s.logger.Infof("用户%s[uid=%d]登录成功", acc.Name, acc.ID)


        acc_log := &model.AccountLog{}
        acc_log.ID = s.idgen.NextID()
        acc_log.Type = account_type.AccountLoginOK
        acc_log.AccountID = acc.ID
        acc_log.AccountName = acc.Name
        acc_log.IP = c.ClientIP()
        acc_log.Result = ""
        if err := s.gorm().Create(acc_log).Error; err != nil {
            s.logger.Error(err)
        }

        c.Set("acc", acc)

        return ginjwt.MapClaims{
            UIDKey: acc.ID,

        }, nil
    case account_type.AuthTypeWXOpenID:
        s.logger.Info("微信小程序登录")
        req := &struct {
            WXOpenID string `form:"wx_openid" json:"wx_openid" binding:"required,min=8"`
        }{}
        if err := c.ShouldBindBodyWith(req, binding.JSON); err != nil {
            s.logger.Error(err)
            return nil, errors.New("missing wx openid")
        }
        s.logger.Infof("微信%s登录", req.WXOpenID)


        acc, err := s.GetAccountByWXOpenID(req.WXOpenID)
        if err != nil {
            s.logger.Error(err.Error())
            return nil, errors.New("db error")
        }
        s.logger.Infof("账号%s[%d]根据wxopenid[%s]登录成功", acc.Name, acc.ID, acc.WxOpenid)


        acc_log := &model.AccountLog{}
        acc_log.ID = s.idgen.NextID()
        acc_log.Type = account_type.AccountLoginOK
        acc_log.AccountID = acc.ID
        acc_log.AccountName = acc.Name
        acc_log.IP = c.ClientIP()
        acc_log.Result = ""
        if err := s.gorm().Create(acc_log).Error; err != nil {
            s.logger.Error(err)
        }

        c.Set("acc", acc)

        return ginjwt.MapClaims{
            UIDKey: acc.ID,

        }, nil
    default:
        return nil, errors.New("invalid authtype " + loginAuthType.AuthType)
    }
}


func (s *Account) Authorizator(data ginjwt.MapClaims, c *gin.Context) bool {
    uid := int64(data[UIDKey].(float64))


    acc, err := s.CacheGetAccountByID(uid)
    if err != nil {
        s.logger.Errorf("cannot find account %s", err.Error())
        return false
    }
    c.Set("acc", acc)


    url_path := c.Request.URL.Path
    method := c.Request.Method
    if strings.HasPrefix(url_path, "/account") {
        s.logger.Infof("白名单 %s", url_path)
        return true
    }


    role_list, err := s.CacheGetAccountRoles(acc.ID)
    if err != nil {
        s.logger.Errorf("获取用户%s[%d]角色失败: %s", acc.Name, acc.ID, err.Error())
        return false
    }

    var pass bool
    for _, role := range role_list {
        if role.Name == account_type.AdminName {
            pass = true
            break
        }
        s.logger.Infof("角色%s[%d]", role.Name, role.ID)
        role_id := strconv.FormatInt(role.ID, 10)

        ok, err := s.csb.Enforce(role_id, url_path, method)
        if err != nil {
            s.logger.Error(err.Error())
            continue
        }

        s.logger.Infof("用户%s[%d][%s]权限检测: %s %s %s 结果: %v", acc.Name, acc.ID, role.Name, role_id, url_path, method, ok)
        if ok {
            pass = true
            break
        }
    }
    if pass {
        return true
    } else {
        return false
    }
}














